/******************************************************************************* 
  Copyright (C), 1988-2020, zeno Tech. Co., Ltd.
  Description:   osd»­Ïß»­¿ò  
  Function List:     
  History: 
*******************************************************************************/ 
/*=============================================================================
                                 Includes                                  
 =============================================================================*/
#include "mediaPriv.h"
#include "mp4v2/mp4v2.h"
#include "ConfigApi.h"
#include "VideoEncode.h"
#include <dirent.h>
#include <iostream>
#include <sys/statfs.h>
#include "AudioEncode.h"
#include "StreamComposeMgr.h"
#include "CurtainActManager.h"

#include <sys/stat.h>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cstring>
#include <unistd.h>
#include <sys/statvfs.h>

using namespace std;

/*=============================================================================
                               Macro Definition
 =============================================================================*/
#define AVAILABLE_DISK_MIN 			(256 * 1024 * 1024)//256M

#define SDMAXUSED 70

#define SDMEDIUM  50

#define MAXSAVETIME 86400

/*=============================================================================
                        Local Structures and Typedef 
 =============================================================================*/ 

/* 帧数据 */
typedef struct
{
    char *pBuf;                 /* 帧数据缓存 */
    unsigned int dataLen;       /* 帧数据长度 */
    int reserved[14];
} Recorder_vencPacket_t;

/* 帧数据 */
typedef struct
{
    char *pBuf;                 /* 帧数据缓存 */
    unsigned int dataLen;       /* 帧数据长度 */
    int reserved[14];
} Recorder_audioPacket_t;

//结构体保存文件夹信息和最后修改时间
struct FolderInfo {
    std::string name;
    std::string path;
    time_t lastModifiedTime;
};

/*=============================================================================
                             Local Variables                                  
 =============================================================================*/  

static const std::string recordPath = "/mnt/sd/RecordFile";
/*=============================================================================
                             Function Declare                               
 =============================================================================*/

/*=============================================================================
                             Function Definition                                  
 =============================================================================*/ 

const std::string RECORDS_DIRECTORY = "/mnt/sd/RecordFile";

static string getSDCardPath() {
    // 请根据实际情况修改SD卡的挂载路径
    return "/mnt/sd";
}

static int calculateSDCardUsage() {
    string sdcardPath = getSDCardPath();

    struct statfs stat;
    if (statfs(sdcardPath.c_str(), &stat) == -1) {
        cerr << "无法获取SD卡信息" << endl;
        return -1;
    }

    // 计算总空间和可用空间
	long long totalSpace = static_cast<long long>(stat.f_bsize) *  static_cast<long long>(stat.f_blocks);  // 计算磁盘的总字节数
    long long freeSpace = static_cast<long long>(stat.f_bsize) *  static_cast<long long>(stat.f_bfree); 

    // 计算已用空间
    long long usedSpace = totalSpace - freeSpace;

    // 计算占用率（百分比）
    int usagePercent =  static_cast<int>(usedSpace  * 100 / totalSpace);

	LOG_PrintWarn("usedSpace:%lld  freeSpace:%lld  totalSpace:%lld    usagePercent:%d\n", usedSpace, freeSpace, totalSpace, usagePercent);

    return usagePercent;
}

//获取文件夹信息和最后修改时间
static std::vector<FolderInfo> getFolderInfos(const std::string& directory) {
    std::vector<FolderInfo> folderInfos;
    DIR* dir = opendir(directory.c_str());
    if (dir == nullptr) {
        perror("Error opening directory");
        return folderInfos;
    }

    struct dirent* entry;
    while ((entry = readdir(dir)) != nullptr) {
        std::string filename = entry->d_name;
        if (filename[0] == '.') {
            continue; // 忽略隐藏文件和目录
        }

        std::string filepath = directory + "/" + filename;
        struct stat filestat;
        if (stat(filepath.c_str(), &filestat) != 0) {
            continue; // 获取文件信息失败
        }

        if (S_ISDIR(filestat.st_mode)) { // 检查是否是目录
            FolderInfo folderInfo;
            folderInfo.name = filename;
            folderInfo.path = filepath;
            folderInfo.lastModifiedTime = filestat.st_mtime;
			//LOG_PrintInfo("filename: %s     folderInfo.lastModifiedTime:%s\n", filename, ctime(&folderInfo.lastModifiedTime));
            folderInfos.push_back(folderInfo);
        }
    }

    closedir(dir);
    return folderInfos;
}

//判断文件是否是当天创建或修改的
static bool isFileModifiedToday(const std::string& filepath) {
    struct stat file_stat;
    if (stat(filepath.c_str(), &file_stat) != 0) {
        return false; // 获取文件信息失败
    }

    time_t current_time = time(nullptr);
    time_t file_time = file_stat.st_mtime; // 文件最后修改时间

	//LOG_PrintWarn("filepath:%s   file_time: %s", filepath.c_str(), ctime(&file_time));

    // 检查文件是否是今天修改的
    return (difftime(current_time, file_time) <= MAXSAVETIME); // 一天有86400秒
}

//删除文件夹和其中的所有文件
static bool deleteFolderAndFiles(const std::string& directory) {
    DIR* dir = opendir(directory.c_str());
    if (dir == nullptr) {
        perror("Error opening directory");
        return false;
    }

    struct dirent* entry;
    while ((entry = readdir(dir)) != nullptr) {
        std::string filename = entry->d_name;
        if (filename[0] == '.') {
            continue; // 忽略隐藏文件和目录
        }

        std::string filepath = directory + "/" + filename;
        struct stat filestat;
        if (stat(filepath.c_str(), &filestat) != 0) {
            continue; // 获取文件信息失败
        }

        if (S_ISREG(filestat.st_mode)) { // 普通文件，检查是否当天修改
            if (!isFileModifiedToday(filepath)) {
                if (remove(filepath.c_str()) != 0) {
                    perror("Error deleting file");
                    return false;
                }
            }
        } else if (S_ISDIR(filestat.st_mode)) { // 子目录，递归删除
            if (!deleteFolderAndFiles(filepath)) {
                return false;
            }
        }
    }

    closedir(dir);

    // 删除空目录
    if (rmdir(directory.c_str()) != 0) {
        perror("Error deleting directory");
        return false;
    }

    return true;
}


static bool compareByLastModifiedTime(const FolderInfo& folder1, const FolderInfo& folder2) {
    return folder1.lastModifiedTime < folder2.lastModifiedTime;
}

static bool deleteFoldersToTarget(const std::string& directory, int targetPercentage) {
	std::vector<FolderInfo> folderInfos = getFolderInfos(directory);

    // 按最后修改时间排序（从早到晚）
    std::sort(folderInfos.begin(), folderInfos.end(), compareByLastModifiedTime);
    struct statvfs diskInfo;

	//LOG_PrintWarn("size:%d\n", folderInfos.size());
    for (const FolderInfo& folderInfo : folderInfos) {
        if (isFileModifiedToday(folderInfo.path)) {
            std::cout << "Skipping folder: " << folderInfo.path << " (modified today)" << std::endl;
            break; // 不删除当天创建或修改的文件夹
        }

        std::cout << "Deleting folder: " << folderInfo.path << std::endl;
        if (!deleteFolderAndFiles(folderInfo.path)) {
            std::cout << "Error deleting folder: " << folderInfo.path << std::endl;
            return false;
        }

        // 检查是否还存在非当天修改的文件
        statvfs(getSDCardPath().c_str(), &diskInfo);
        long long totalBytes = static_cast<long long>(diskInfo.f_frsize) * diskInfo.f_blocks;  // 计算磁盘的总字节数
        long long freeBytes = static_cast<long long>(diskInfo.f_frsize) * diskInfo.f_bavail;  // 计算磁盘的可用字节数
        long long usedBytes = totalBytes - freeBytes;                                        // 计算磁盘的已用字节数
        long long targetBytes = static_cast<long long>((totalBytes * targetPercentage) / 100);

        if (usedBytes <= targetBytes) {
			return true; // 已达到目标内存值，结束删除
        }

    }
    return false;
}


static bool ReSizeSD(){
	//判断当前sd卡的使用达到阈值，就将保存的MP4文件删除到某一个阈值
	if(SDMAXUSED < calculateSDCardUsage()){
		//调用接口删除，并且返回删除的结果
		return deleteFoldersToTarget(RECORDS_DIRECTORY, SDMEDIUM);
	}
	return true;
}


static void creatRecordDir(const char* dir)
{
    DIR* fpDir = opendir(dir);
	if(!fpDir)
	{
		mkdir(dir, 0775);
	}
	else
	{
		closedir(fpDir);
	}
}

static unsigned long long getFileSize(const char *path)
{
	unsigned long long filesize = -1;
	struct stat statbuff;
	if (stat(path, &statbuff) < 0)
	{
		return filesize;
	}
	else
	{
		filesize = statbuff.st_size;
	}
	return filesize;
}

static int HexPrint(const char *str,  const unsigned char *data, int len)
{
    int i= 0;

    printf("%s(%d):\n", str, len);
    for(i = 1; i <= len; i++)
    {
        printf("%02x ", data[i - 1]);
        if (i%16 == 0)
            printf("\n");
    }
    printf("\n");
    return 0;
}

static unsigned long long getMonotonicTimeMs(void)
{
    struct timespec time = {0};

    clock_gettime(CLOCK_MONOTONIC, &time);
    return (time.tv_sec * 1000 + time.tv_nsec/1000000);
}

static long long getSDCardAvailableDisk(void)
{
	struct statfs diskInfo;
	long long blocksize;
	long long availableDisk;
	
	statfs("/mnt/sd", &diskInfo);
	blocksize = diskInfo.f_bsize;
	availableDisk = diskInfo.f_bavail * blocksize;
	return availableDisk;
}

static int checkSDCardExsit(void)
{
	FILE *fp = NULL;
	int  exist = 0;
	char buf[1024] = {0};

	fp = fopen("/proc/mounts", "r");
	if (fp == NULL)
	{
		return false;
	}
	fread(buf, sizeof(buf), 1, fp);
	if (strstr(buf, "/mnt/sd"))
	{
		exist = 1;
	}
	else
	{
		exist = 0;
	}
	fclose(fp);

	printf("checkSDCardExsit exist=%d\n", exist);
	return exist;
}

static int h265_getNalu(unsigned char *pNalu, int dataLen)
{
    unsigned char c;
    int pos = 0;
    int len;

    if(pNalu[0] != 0 || pNalu[1] != 0 || pNalu[2] != 0 || pNalu[3] != 1)
        return -1;

    pos = 4;
    while(dataLen--)
    {
        if(pNalu[pos-3] == 0 && pNalu[pos-2] == 0 && pNalu[pos-1] == 0 && pNalu[pos] == 1)
        {
            pos -= 4;
            break;
        }
        pos++;
    }
    len = pos + 1;
    return len;
}

const uint8_t* find_next_nal(const uint8_t* start, const uint8_t* end)
{
   const uint8_t *p = start;
   /* Simply lookup "0x000001" or "0x00000001" pattern */
   while (p <= end - 3) {
      if (p[0] == 0x00) {
         if (p[1] == 0x00) {
            if ((p[2] == 0x01) || (p[2] == 0x00 && p[3] == 0x01)) {
               return p;
            } else {
               p += 3;
            }
         } else {
            p += 2;
         }
      } else {
         ++p;
      }
   }
   return end;
}

void get_h264_sps_pps_nalu(uint8_t *data, int len, std::vector<uint8_t> &sps, std::vector<uint8_t> &pps)
{
    const uint8_t* pkt_bgn = data;
    const uint8_t* pkt_end = pkt_bgn + len;
    const uint8_t* pbgn = pkt_bgn;
    const uint8_t* pend = pkt_end;
    while (pbgn < pkt_end) {
        pbgn = find_next_nal(pbgn, pkt_end);
        if (pbgn == pkt_end) {
           continue;
        } else {
           while (*pbgn == 0) ++pbgn; //skip all 0x00
           ++pbgn; //skip 0x01
        }
        pend = find_next_nal(pbgn, pkt_end);

        if (((*pbgn) & 0x1F) == 0x07) {    //SPS NAL
            //std::cout<<"find sps nal"<<std::endl;
            sps.assign(pbgn, pbgn + static_cast<int>(pend - pbgn));
        }
        if (((*pbgn) & 0x1F) == 0x08) {    //PPS NAL
            //std::cout<<"find pps nal"<<std::endl;
            pps.assign(pbgn, pbgn + static_cast<int>(pend - pbgn));
       }
       pbgn = pend;
    }
}

static void *MEDIA_transToMp4Proc(void *arg)
{
	MEDIA_ctl_t *mediaCtl = (MEDIA_ctl_t *)arg;
	MEDIA_listBufInfo_t *pListFrame;
	unsigned char *pframeBuf = NULL;
	MP4FileHandle file;
	MP4TrackId video;
	MP4TrackId audio;
	int frameLen = 0;
	unsigned char h264_sps[17] = {0};
	unsigned char h265_vps[32] = {0};
	unsigned char h265_sps[64] = {0};
	unsigned char h265_pps[16] = {0};
	int vps_len = 0;
	int sps_len = 0;
	int pps_len = 0;
	int first = 1;
	int isSyncSample = 0;
	char *pMp4info = NULL;
	char *p = NULL;
	char *q = NULL;
	char videotime[16] = {0};
	int j = 0;
	int mp4FileLoss = 0;
	char tmp[16] = {0};
	char complateName[64] = {0};
	VideoEncFormat encFormat = {0};
	AudioEncDesc *encDesc = &mediaCtl_g.audioCtl.encDesc[RTSP_AUDIO_CHN];
	AudioEncFormat aencformat = {0};
	struct timeval start;
	struct timeval end;
	int  autoCutFlag = 0;
	uint64_t  mp4FileSize = 0;
	char *pAacConfig = NULL;
	unsigned long pAacConfigSize = 0;
	char *pBrand[5];	
	pBrand[0] = "isom";
	pBrand[1] = "iso2";
	pBrand[2] = "avc1";
	pBrand[3] = "mp41";

    prctl(PR_SET_NAME, __func__);
    LOG_PrintInfo("tid:%d\n", gettid());

    Media::VideoEncode::instance(SD_OUT)->getFormat(&encFormat);
    Media::AudioEncode::instance(SD_OUT)->getFormat(&aencformat);//通道待定
    
	LOG_PrintInfo("type: %d, fps: %d, width: %d, height:%d, AiSampleRate: %d, AiChannels: %d\n", 
					encFormat.type, (int)encFormat.fps, encFormat.width, encFormat.height, aencformat.frequency, aencformat.channelNum);
    //file = MP4Create(mediaCtl_g.mp4Name, 0);
    file = MP4CreateEx(mediaCtl_g.mp4Name, 0, 1, 1, "isom", 0x00000200, pBrand, 4);
    if (file == MP4_INVALID_FILE_HANDLE)
    {
        LOG_PrintError("open file fialed.\n");
        return NULL;
    }
    MP4SetTimeScale(file, 1000);

    //MP4_MPEG4_AAC_MAIN_AUDIO_TYPE
    audio = MP4AddAudioTrack(file, aencformat.frequency, aencformat.channelNum * 1024, MP4_MPEG4_AUDIO_TYPE); //MP4_MPEG2_AAC_LC_AUDIO_TYPE
    if (audio == MP4_INVALID_TRACK_ID)
    {
        LOG_PrintError("add audio track fialed.\n");
        //return;
    }
    MP4SetAudioProfileLevel(file, 0x2);

    Media::AudioEncode::instance(SD_OUT)->getSpecificInfo(&pAacConfig, &pAacConfigSize);
    if (!pAacConfig)
    {
    	LOG_PrintError("AudioEncode getSpecificInfo fialed.\n");
    }
    else
    {
		MP4SetTrackESConfiguration(file, audio, pAacConfig, pAacConfigSize);
	}
	
	while(mediaCtl->isRecording)
	{
		if (access("/dev/mmcblk2", F_OK))
		{
			mp4FileLoss = 1;
			MEDIA_RecorderStop_Core();
			LOG_PrintWarn("SD card is removed, Stop Record!\n");
			break;
		}
		
	    pthread_mutex_lock(&mediaCtl->bufLock);
	   	isSyncSample = 0;
	    if(!list_empty(&mediaCtl->fullBuf))
	    {
	        pListFrame = list_first_entry(&mediaCtl->fullBuf, MEDIA_listBufInfo_t, list);
	        list_del(&pListFrame->list);
	        pthread_mutex_unlock(&mediaCtl->bufLock);

	        if (pListFrame->mediaType == MEDIA_TYPE_VIDEO)
	        {
				 //FILE_write("/mnt/sd/mp4test.h264", pListFrame->pBuf, pListFrame->len);
				 //MP4WriteSample(file, video, pListFrame->pBuf, pListFrame->len, (33) * 90000/1000, 0, 1);
			    pframeBuf = pListFrame->pBuf;
				frameLen = pListFrame->len;
				/*
				if (videoEncH265 == encFormat->type)
				{
					if (pListFrame->type == videoEncFrameI)
					{
						vps_len = h265_getNalu(pframeBuf, frameLen);
						memcpy(h265_vps, pframeBuf + 4, vps_len - 4);
					
						pframeBuf += vps_len;
						frameLen  -= vps_len;
						
						sps_len = h265_getNalu(pframeBuf, frameLen);
						memcpy(h265_sps, pframeBuf + 4, sps_len - 4);
				    
						pframeBuf += sps_len;
						frameLen  -= sps_len;
						
						pps_len = h265_getNalu(pframeBuf, frameLen);
						memcpy(h265_pps, pframeBuf + 4, pps_len - 4);

						pframeBuf += pps_len;
						frameLen  -= pps_len;
					    if(first)
					    {
							video = MP4AddH265VideoTrack(file, 90000, 90000/(int)encFormat->fps, encFormat->width, encFormat->height,
									                        h265_vps[2],               // vps[2] AVCProfileIndication
									                        h265_vps[3],               // vps[3] profile_compat
									                        h265_vps[4],               // vps[4] AVCLevelIndication
									                        3);                        // 4 bytes length before each NAL unit
							if (video == MP4_INVALID_TRACK_ID)
							{
							    LOG_PrintError("add video track fail.\n");
							    return;
							}
							MP4SetVideoProfileLevel(file, 0x7F);
							MP4AddH265VideoParameterSet(file, video, h265_vps, vps_len - 4);
							//HexPrint("vps", h265_vps, vps_len - 4);
							first = 0;
						}
						MP4AddH265SequenceParameterSet(file, video, h265_sps, sps_len - 4);
					    //HexPrint("sps", h265_sps, sps_len - 4);
						MP4AddH265PictureParameterSet(file, video, h265_pps, pps_len - 4);
						//HexPrint("pps", h265_pps, pps_len - 4);
						isSyncSample = 1;
					}
	                //HexPrint("data", pframeBuf, 64);	
				}
				else */if (videoEncH264 == encFormat.type)
				{
					if(pListFrame->type == videoEncFrameI)
					{
						std::vector<uint8_t> sps_;
						std::vector<uint8_t> pps_;
						get_h264_sps_pps_nalu((uint8_t *)pframeBuf, 128, sps_, pps_);//sps_.size(), 
						memcpy(h264_sps, sps_.data(), sizeof(h264_sps));
					    if(first)
					    {
						    video = MP4AddH264VideoTrack(file, 90000, 90000/(int)encFormat.fps, encFormat.width, encFormat.height,
							                                            h264_sps[1], //sps[1] AVCProfileIndication
							                                            h264_sps[2], //sps[2] profile_compat
							                                            h264_sps[3], //sps[3] AVCLevelIndication
							                                            3);     // 4 bytes length before each NAL unit
						    if (video == MP4_INVALID_TRACK_ID)
						    {
						        LOG_PrintError("add video track fail.\n");
						        //return;
						    }
						    MP4SetVideoProfileLevel(file, 0x7F);
							//HexPrint("sps", h264_sps, 17);
							first = 0;
						}
						//HexPrint("sps", sps_.data(), sps_.size());
						//HexPrint("pps", pps_.data(), pps_.size());
						MP4AddH264SequenceParameterSet(file, video, sps_.data(), sps_.size());
						MP4AddH264PictureParameterSet(file, video, pps_.data(), pps_.size());
						isSyncSample = 1;
						pframeBuf += (sps_.size() + pps_.size() + 8); //sps与pps只写在头部，此处跳过
						frameLen  -= (sps_.size() + pps_.size() + 8);
					}
				}
                pframeBuf[0] = ((frameLen - 4)>>24)&0xFF;
                pframeBuf[1] = ((frameLen - 4)>>16)&0xFF;
                pframeBuf[2] = ((frameLen - 4)>>8)&0xFF;
                pframeBuf[3] = ((frameLen - 4)>>0)&0xFF;
				gettimeofday(&start, NULL);
				MP4WriteSample(file, video, pframeBuf, frameLen, pListFrame->duration, 0, isSyncSample);
				gettimeofday(&end,NULL);
				if ((end.tv_sec * 1000 + end.tv_usec/1000) - (start.tv_sec * 1000 + start.tv_usec/1000) > 200)
					printf("time used is %d ms\n", (end.tv_sec * 1000 + end.tv_usec/1000) - (start.tv_sec * 1000 + start.tv_usec/1000));
	        }
	        else if(pListFrame->mediaType == MEDIA_TYPE_AUDIO)
	        {
	        	 //MP4WriteSample(file, audio, pListFrame->pBuf, pListFrame->len , (20) * 44100/1000, 0, 1);
	        	MP4WriteSample(file, audio, pListFrame->pBuf + 7, pListFrame->len - 7 , pListFrame->duration, 0, 1); //aac头部7字节不传输
	        }
	        pthread_mutex_lock(&mediaCtl->bufLock);
	        list_add_tail(&pListFrame->list, &mediaCtl->emptyBuf);
	        pthread_mutex_unlock(&mediaCtl->bufLock);
	    }
	    else
	    {
	        pthread_mutex_unlock(&mediaCtl->bufLock);
	        usleep(1 * 1000);
	    }
	    //fat32 sigle file size can't greater than 4G
	    mp4FileSize = getFileSize(mediaCtl_g.mp4Name);
	    if (mp4FileSize > (1024 * 1024 * 1024))
	    {
	    	MEDIA_RecorderStop_Core();
	    	autoCutFlag = 1;
	    	usleep(200 * 1000);
	    	LOG_PrintWarn("mediaCtl_g.mp4Name:%s, file size %llu, auto Cut !\n", mediaCtl_g.mp4Name, mp4FileSize);
	    }
	}
	if (!mp4FileLoss)
	{
		pMp4info = MP4Info(file, video);
		p = strchr(pMp4info, ',');
		q = strstr(pMp4info, "sec");
		if (p && q)
		{
			strncpy(tmp, p + 1, q - p - 1);
			for(int i = 0; i < strlen(tmp); i++)
			{
				if(tmp[i] == '.' || tmp[i] == ' ')
				{
					continue;
				}
				videotime[j++] = tmp[i];
			}
			tracef("Mp4 time is %s ms\n", videotime);
			free(pMp4info);
		}
		MP4Close(file, MP4_CLOSE_DO_NOT_COMPUTE_BITRATE);
	#if 0
		sprintf(complateName, "%s%s.mp4", mediaCtl_g.mp4Name, videotime);
	#else
		sprintf(complateName, "%s.mp4", mediaCtl_g.mp4Name);
	#endif
		rename(mediaCtl_g.mp4Name, complateName);
		sync();
		LOG_PrintInfo("Mp4 close %s\n", complateName);
		if (autoCutFlag)
		{
			autoCutFlag = 0;
			MEDIA_RecorderStart_Core();
		}
	}
	pthread_exit(NULL);
	return NULL;
}

static int MEDIA_sendToMp4Muxer(char *pData, int len, int mediaType, int duration, int type)
{
    MEDIA_listBufInfo_t *pListFrame;
    MEDIA_ctl_t *mediaCtl = &mediaCtl_g;

    if (len > MAX_MEDIA_BUF_LEN)
    {
        LOG_PrintError("bufLen is invalid,(%d, %d)\n", len, MAX_MEDIA_BUF_LEN);
        return -1;
    }

    pthread_mutex_lock(&mediaCtl->bufLock);
    if(!list_empty(&mediaCtl->emptyBuf))
    {
        pListFrame = list_first_entry(&mediaCtl->emptyBuf, MEDIA_listBufInfo_t, list);
        list_del(&pListFrame->list);
        pthread_mutex_unlock(&mediaCtl->bufLock);

        pListFrame->mediaType = mediaType;
        pListFrame->len = len;
        pListFrame->duration = duration;
        pListFrame->type     = type;
        memcpy(pListFrame->pBuf, pData, len);

        pthread_mutex_lock(&mediaCtl->bufLock);
        list_add_tail(&pListFrame->list, &mediaCtl->fullBuf);
        pthread_mutex_unlock(&mediaCtl->bufLock);
    }
    else
    {
        pthread_mutex_unlock(&mediaCtl->bufLock);
        LOG_PrintWarn("emptyBuf list is empty!\n");
    }
    return 0;
}


/*******************************************************************************
* 函数名  : MEDIA_RecorderGetPacket
* 描  述  : 获取编码数据
* 输  入  : 无。
* 输  出  : 无。
* 返回值  : 0:  成功。
*           -1: 失败。
*******************************************************************************/
static int MEDIA_RecorderGetPacket(Recorder_vencPacket_t *pPacket)
{
    int status;
    VideoEncPacket encPacket;
    int len;
    VideoEncBuf	buffer;
    int dataLen;
    char *pData;
	static unsigned long long nowvoltime = 0;
	static unsigned long long videovoltime = 0;
	unsigned long long duration = 0;

    //buffer.buffer = (unsigned char *)mediaCtl_g.pEncBuf[SD_OUT];
    //buffer.len = len;
    encPacket.vBuffer = &buffer;
    encPacket.vSize = 1;

    status = mediaCtl_g.pVideoEncoder[SD_OUT]->getPacket(mediaCtl_g.pVideoEncoder[SD_OUT], 
                                                   &encPacket);
    if (status)
    {
        return -1;
    }

    pData = (char *)encPacket.vBuffer[0].buffer;
    dataLen = encPacket.vBuffer[0].len;
    
    pPacket->pBuf = pData;
    pPacket->dataLen = dataLen;
    //HexPrint("get", pPacket->pBuf, 128);

	nowvoltime = getMonotonicTimeMs();
	if (nowvoltime - videovoltime > 200)
	{
		duration = 0;
	}
	else
	{
		duration = (nowvoltime - videovoltime)*90000/1000;
	}
	status = MEDIA_sendToMp4Muxer(pPacket->pBuf, pPacket->dataLen, MEDIA_TYPE_VIDEO, duration, encPacket.type);
    if (status)
    {
        LOG_PrintError("Fail to MEDIA_sendToMp4Muxer.\n");
        return -1;
    }
    videovoltime = nowvoltime;

    mediaCtl_g.pVideoEncoder[SD_OUT]->releasePacket(mediaCtl_g.pVideoEncoder[SD_OUT]);
    return 0;
}
/*******************************************************************************
* 函数名  : MEDIA_RecorderReleasePacket
* 描  述  : 释放编码数据
* 输  入  : 无。
* 输  出  : 无。
* 返回值  : 0:  成功。
*           -1: 失败。
*******************************************************************************/
static int MEDIA_RecorderReleasePacket(void)
{    
    mediaCtl_g.pVideoEncoder[SD_OUT]->releasePacket(mediaCtl_g.pVideoEncoder[SD_OUT]);
  
    return 0;
}

/*******************************************************************************
* 函数名  : MEDIA_RecorderStop_Core
* 描  述  : 停止编码
* 输  入  : 无。
* 输  出  : 无。
* 返回值  : 0:  成功。
*           -1: 失败。
*******************************************************************************/
int MEDIA_RecorderStop_Core(void)
{
    // int status;

	// if (mediaCtl_g.isRecording == 0 || !checkSDCardExsit())
	// {
	// 	LOG_PrintWarn("no Recording or SD card not exist!");
	// 	return -1;
	// }

    // status = mediaCtl_g.pVideoEncoder[SD_OUT]->stop(mediaCtl_g.pVideoEncoder[SD_OUT]);
    // if (status)
    // {
    //     LOG_PrintInfo("Fail to start encoder!\n");
    //     return -1;
    // }

	// mediaCtl_g.isRecording = 0;

	// LOG_PrintWarn("MEDIA_RecorderStop_Core success!\n");
    return 0;
}

/*******************************************************************************
* º¯ÊýÃû  : MEDIA_RecorderGetAudioPacket
* Ãè  Êö  : »ñÈ¡±àÂëÊý¾Ý
* Êä  Èë  : ÎÞ¡£
* Êä  ³ö  : ÎÞ¡£
* ·µ»ØÖµ  : 0:  ³É¹¦¡£
*           -1: Ê§°Ü¡£
*******************************************************************************/
extern "C" int MEDIA_RecorderAudio(AudioEncDesc *encDesc, char *pBuf, int len)
{
	int status = 0;
	static unsigned long long nowvoltime = 0;
	static unsigned long long audiovoltime = 0;
	unsigned long long duration = 0;

	if (0 == mediaCtl_g.isRecording)
	{
		return 0;
	}

	if (!encDesc || !pBuf)
	{
		LOG_PrintError("encDesc or  pBuf NULL\n");
		return -1;
	}

    AudioEncFormat aencformat = {0};
	Media::AudioEncode::instance(SD_OUT)->getFormat(&aencformat);//通道待定
	
	nowvoltime = getMonotonicTimeMs();
	if (nowvoltime - audiovoltime > 200)
	{
		duration = 0;
	}
	else
	{
		duration = (nowvoltime - audiovoltime) * aencformat.frequency/1000;
	}
	status = MEDIA_sendToMp4Muxer(pBuf, len, MEDIA_TYPE_AUDIO, duration, 0);
	if (status)
	{
		LOG_PrintError("err: opSendToMp4Muxer fail\n");
		return -1;
	}
	audiovoltime = nowvoltime;
    return 0;
}

static void *MEDIA_videoSendToRecorderPtd(void *arg)
{
    int ret;
    Recorder_vencPacket_t vencPacket;
    long long availableDisk = 0;
    
	pthread_detach(pthread_self());
    prctl(PR_SET_NAME, __func__);
    LOG_PrintInfo("tid:%d\n", gettid());
    
    while(mediaCtl_g.isRecording)
    {
        ret = MEDIA_RecorderGetPacket(&vencPacket);
        if (ret < 0)
        {
            usleep(10*1000);
            continue;
        }

        
        availableDisk = getSDCardAvailableDisk();
		if (availableDisk < AVAILABLE_DISK_MIN) // less 256M, stop
		{
			MEDIA_RecorderStop_Core();
			LOG_PrintWarn("SD: space is not enough, Stop!\n");
		}
    }
    pthread_exit(NULL);

    return NULL;
}

static void *MEDIA_audioSendToRecorderPtd(void *arg)
{
    int ret;
	char audioBuf[2048] = {0};
    int audioLen = 0;
	AudioEncDesc *encDesc = &mediaCtl_g.audioCtl.encDesc[RTSP_AUDIO_CHN];
	bool iRet = false;
    
	pthread_detach(pthread_self());
    prctl(PR_SET_NAME, __func__);
    LOG_PrintInfo("tid:%d\n", gettid());
    
    while(mediaCtl_g.isRecording)
    {
        iRet = Media::AudioEncode::instance(SD_OUT)->getPacket(audioBuf, &audioLen);
        if (!iRet)
        {
            usleep(5*1000);
            continue;
        }

        //media_FILE_Write("wyg/record.aac",audioBuf, audioLen);

        MEDIA_RecorderAudio(encDesc, audioBuf, audioLen);
    }
    pthread_exit(NULL);

    return NULL;
}

/*******************************************************************************
* 函数名  : MEDIA_RecorderStart_Core
* 描  述  : 启动编码
* 输  入  : 无。
* 输  出  : 无。
* 返回值  : 0:  成功。
*           -1: 失败。
*******************************************************************************/
int MEDIA_RecorderStart_Core(void)
{
//     int status;
// 	pthread_t Ptd;
// 	pthread_t recordPtd;
// 	char dir[64] = {0};
// 	DIR* fpDir = NULL;
// 	std::string dirPath;
	
// 	time_t timer;
// 	struct tm *Now;
// 	long long availableDisk = 0;
// 	bool result = false;

// 	if (mediaCtl_g.isRecording || !checkSDCardExsit())
// 	{
// 		LOG_PrintWarn("Uvc is opening or Recording or SD card not exist, mediaCtl_g.isRecording=%d!\n", mediaCtl_g.isRecording);
// 		return -1;
// 	}

// 	ReSizeSD();
	
// 	availableDisk = getSDCardAvailableDisk();
// 	if (availableDisk < AVAILABLE_DISK_MIN)
// 	{
// 		LOG_PrintWarn("SD: space is not enough, Stop!\n");
// 		return -1;
// 	}

// 	time(&timer);
// 	Now = localtime(&timer);

//     dirPath = recordPath;
// 	creatRecordDir(dirPath.c_str());

// 	sprintf(dir, "/%d-%02d-%02d", (Now->tm_year + 1900), Now->tm_mon + 1, Now->tm_mday);
// 	dirPath += dir; 
// 	creatRecordDir(dirPath.c_str());
		
// 	sprintf(mediaCtl_g.mp4Name, "%s/%d%02d%02d_%02d%02d%02d", dirPath.c_str(), (Now->tm_year + 1900),
// 							Now->tm_mon + 1, Now->tm_mday, Now->tm_hour, Now->tm_min, Now->tm_sec);
// 	mediaCtl_g.isRecording = 1;

// #if 1
//     status = pthread_create(&recordPtd, NULL, MEDIA_transToMp4Proc, (void *)&mediaCtl_g);
//     if (status < 0)
//     {           
//         LOG_PrintError("pthread_create rtcp error:\n");
//         return -1;
//     }
// #endif

// #if 1
//     VideoEncRefOptions refOptions;
//     VideoEncFormat	videoFormat;
//     memset(&videoFormat, 0, sizeof(videoFormat));
//     memset(&refOptions, 0, sizeof(refOptions));
       
//     videoFormat.type = videoEncH264;
//     videoFormat.width = 1920;
//     videoFormat.height = 1080;
//     videoFormat.bitrateControl = videoEncBitrateCtrlConstant;
//     videoFormat.fps = 30; 
//     videoFormat.bitrate = (1*videoFormat.width*videoFormat.height/1024)*videoFormat.fps/30;
//     videoFormat.gop = videoFormat.fps*2;
//     videoFormat.quality = 1;
//     videoFormat.qualityType = 0;
//     videoFormat.pack = videoEncPackTypeRAW;
//     videoFormat.profile = h264ProfileHigh;
//     videoFormat.svcType = videoSvcInvalid;
// 	refOptions.mode = videoNoRefMode;
// 	videoFormat.refOptions = &refOptions;

// 	status = mediaCtl_g.pVideoEncoder[SD_OUT]->setFormat(mediaCtl_g.pVideoEncoder[SD_OUT], &videoFormat);
//     if (status)
//     {
//         LOG_PrintInfo("Fail to start encoder!\n");
//         return -1;
//     }

//     status = mediaCtl_g.pVideoEncoder[SD_OUT]->start(mediaCtl_g.pVideoEncoder[SD_OUT]);
//     if (status)
//     {
//         LOG_PrintInfo("Fail to start video encoder!\n");
//         return -1;
//     }

//     status = pthread_create(&Ptd, NULL, MEDIA_videoSendToRecorderPtd, &timer);
//     if (status < 0)
//     {           
//         LOG_PrintError("pthread_create rtcp error:\n");
//         return -1;
//     }
// #endif

// #if 1
//     AudioEncFormat encFormat;
//     memset(&encFormat, 0, sizeof(encFormat));
// 	encFormat.type = audioEncAAC;
//     encFormat.pack = audioEncPackTypeADTS; //考虑rtsp同时使用，此处还是ADTS格式,写的时候处理
//     encFormat.frequency = 48000;
//     encFormat.channelNum = 2; //MP4目前只能双通道
//     encFormat.depth = 16;
//     result = Media::AudioEncode::instance(SD_OUT)->setFormat(&encFormat);
//     if (!result)
//     {
//         LOG_PrintInfo("Fail to setFormat audio encoder!\n");
//         return -1;
//     }
    
//     result = Media::AudioEncode::instance(SD_OUT)->start();
//     if (!result)
//     {
//         LOG_PrintInfo("Fail to start audio encoder!\n");
//         return -1;
//     }
    
//     status = pthread_create(&Ptd, NULL, MEDIA_audioSendToRecorderPtd, &timer);
//     if (status < 0)
//     {           
//         LOG_PrintError("pthread_create rtcp error:\n");
//         return -1;
//     }
// #endif

    LOG_PrintWarn("MEDIA_RecorderStart_Core success!\n");
	
    return 0;
}

/*******************************************************************************
* 函数名  : VIDEOTEST_RecorderInit
* 描  述  : 初始化mp4录制器。
* 输  入  : - pVideoEncoder:  编码接口句柄
* 输  出  : 无。
* 返回值  : 0:  成功。
*           -1: 失败。
*******************************************************************************/
int MEDIA_RecorderInit(void)
{	
    MEDIA_ctl_t *mediaCtl = &mediaCtl_g;

    INIT_LIST_HEAD(&mediaCtl->emptyBuf);
    INIT_LIST_HEAD(&mediaCtl->fullBuf);
    pthread_mutex_init(&mediaCtl->bufLock, NULL);
    for (int i = 0; i < MAX_MEDIA_QUE_DEEP; i++)
    {
        mediaCtl->listBuf[i].pBuf = (char *)malloc(MAX_MEDIA_BUF_LEN);
        if (!mediaCtl->listBuf[i].pBuf)
        {
            LOG_PrintError("Fail to malloc!\n");
            return -1;
        }
	    list_add_tail(&mediaCtl->listBuf[i].list, &mediaCtl->emptyBuf);
	}

    return 0;
}

int MEDIA_RecorderStart(void)
{	
	printf("!!!!!!!!!!! MEDIA_RecorderStart\n");
	// Json::Value curtainTable;
	// Media::CurtainActManager::instance()->getSpecificNumCurtain(curtainTable,Media::RecordedBroadcastNum);
	// Media::CurtainActManager::instance()->setCurtainLayout(curtainTable);
	// Json::Value root2 = Json::Value::null;
    // root2["Number"] = Media::RecordedBroadcastNum;
    // root2["PlayMode"] = 0;
    // root2["Action"] = 1;
    // Media::CurtainActManager::instance()->setPlayStrategy(root2);
	// MEDIA_RecorderStart_Core();
	return 0;
}


int MEDIA_RecorderStop(void)
{
	printf("!!!!!!!!!!! MEDIA_RecorderStop\n");
	// Json::Value root2 = Json::Value::null;
	// root2["Number"] = Media::RecordedBroadcastNum;
	// root2["PlayMode"] = 0;
	// root2["Action"] = 0;
	// Media::CurtainActManager::instance()->setPlayStrategy(root2);
	// MEDIA_RecorderStop_Core();
	return 0;
}



int DEV_GetRecordStatus(void)
{	
	return mediaCtl_g.isRecording;
}


